package com.agileboot.common.utils.poi;

import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode.Internal;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.PageReadListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

/**
 * Excel相关处理工具类
 *
 * @author agileboot
 */
@Slf4j
public class CustomExcelUtil {

    /**
     * 读取Excel文件内容
     *
     * @param clazz 目标类型
     * @param file  上传的Excel文件
     * @param <T>   目标类型
     * @return 目标类型的对象列表
     */
    public static <T> List<T> readFromRequest(Class<T> clazz, MultipartFile file) {
        try (InputStream inputStream = file.getInputStream()) {
            // 同步读取
            return EasyExcel.read(inputStream)
                .head(clazz)
                .sheet()
                .doReadSync();
        } catch (Exception e) {
            log.error("Excel文件读取失败", e);
            throw new ApiException(e, Internal.EXCEL_PROCESS_ERROR, e.getMessage());
        }
    }

    /**
     * 将数据写入到Excel并发送到响应
     *
     * @param list     要导出的数据列表
     * @param clazz    数据类型
     * @param response HTTP响应
     * @param <T>      数据类型
     */
    public static <T> void writeToResponse(List<T> list, Class<T> clazz, HttpServletResponse response) {
        try {
            // 直接写入到输出流
            EasyExcel.write(response.getOutputStream(), clazz)
                .sheet("Sheet1")
                .doWrite(list);
        } catch (IOException e) {
            log.error("Excel文件写入失败", e);
            throw new ApiException(e, Internal.EXCEL_PROCESS_ERROR, e.getMessage());
        }
    }

    /**
     * 读取Excel文件内容（异步处理大量数据时使用）
     *
     * @param clazz  目标类型
     * @param file   上传的Excel文件
     * @param <T>    目标类型
     * @return 目标类型的对象列表
     */
    public static <T> List<T> readFromRequestWithBatch(Class<T> clazz, MultipartFile file) {
        final List<T> resultList = new ArrayList<>();
        try (InputStream inputStream = file.getInputStream()) {
            // 异步分批读取，每次处理100条数据
            Consumer<List<T>> consumer = dataList -> {
                resultList.addAll(dataList);
                log.info("成功读取到一批数据，条数：{}", dataList.size());
            };
            
            EasyExcel.read(inputStream, clazz, new PageReadListener<T>(consumer))
                .sheet()
                .doRead();
            return resultList;
        } catch (Exception e) {
            log.error("Excel文件读取失败", e);
            throw new ApiException(e, Internal.EXCEL_PROCESS_ERROR, e.getMessage());
        }
    }

    /**
     * 使用模板导出Excel
     *
     * @param list      要导出的数据列表
     * @param clazz     数据类型
     * @param response  HTTP响应
     * @param templateFile 模板文件路径
     * @param <T>       数据类型
     */
    public static <T> void writeWithTemplate(List<T> list, Class<T> clazz, HttpServletResponse response, String templateFile) {
        try {
            // 使用模板导出
            EasyExcel.write(response.getOutputStream(), clazz)
                .withTemplate(templateFile)
                .sheet()
                .doWrite(list);
        } catch (IOException e) {
            log.error("Excel模板导出失败", e);
            throw new ApiException(e, Internal.EXCEL_PROCESS_ERROR, e.getMessage());
        }
    }
    
    /**
     * 将数据写入到输出流
     *
     * @param list     要导出的数据列表
     * @param clazz    数据类型
     * @param outputStream 输出流
     * @param <T>      数据类型
     */
    public static <T> void writeToOutputStream(List<T> list, Class<T> clazz, OutputStream outputStream) {
        try {
            // 直接写入到输出流
            EasyExcel.write(outputStream, clazz)
                .sheet("Sheet1")
                .doWrite(list);
        } catch (Exception e) {
            log.error("Excel文件写入失败", e);
            throw new ApiException(e, Internal.EXCEL_PROCESS_ERROR, e.getMessage());
        }
    }
    
    /**
     * 从输入流读取Excel文件内容
     *
     * @param clazz 目标类型
     * @param inputStream 输入流
     * @param <T>   目标类型
     * @return 目标类型的对象列表
     */
    public static <T> List<T> readFromInputStream(Class<T> clazz, InputStream inputStream) {
        try {
            // 同步读取
            return EasyExcel.read(inputStream)
                .head(clazz)
                .sheet()
                .doReadSync();
        } catch (Exception e) {
            log.error("Excel文件读取失败", e);
            throw new ApiException(e, Internal.EXCEL_PROCESS_ERROR, e.getMessage());
        }
    }
} 